package com.zlc.workflow.core.instance.cmd;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zlc.common.common.constant.WorkflowConstant;
import com.zlc.common.core.process.model.entity.StoneTask;
import com.zlc.common.core.process.model.vo.StoneProcessInstanceVo;
import com.zlc.common.core.process.model.vo.StoneProcessVo;
import com.zlc.common.core.process.model.vo.StoneTaskVo;
import com.zlc.common.core.process.service.StoneProcessInstanceService;
import com.zlc.common.core.process.service.StoneProcessLogService;
import com.zlc.common.core.process.service.StoneProcessService;
import com.zlc.common.core.process.service.StoneTaskService;
import com.zlc.common.model.user.UserInfo;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.common.util.JsonHelper;
import com.zlc.common.util.UserHelper;
import com.zlc.workflow.constant.ProcessLogTypeEnum;
import com.zlc.workflow.core.instance.model.NotifyWaitNodeParam;
import com.zlc.workflow.core.instance.model.QueryNextTaskParam;
import com.zlc.workflow.core.instance.model.SubmitFlowParam;
import com.zlc.workflow.core.instance.model.WorkflowDetail;
import com.zlc.workflow.core.instance.model.WorkflowTask;
import com.zlc.workflow.core.instance.model.WorkflowTaskNode;
import com.zlc.workflow.flow.NodeModel;
import com.zlc.workflow.flow.NodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 唤醒节点
 */
@Slf4j
public class NotifyNodeCmd extends AbstractCmd<WorkflowDetail> {

    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private StoneProcessInstanceService stoneProcessInstanceService;
    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessLogService stoneProcessLogService;

    private final NotifyWaitNodeParam param;

    private final WorkflowDetail result;

    private final String processInstanceId;

    private final StoneProcessInstanceVo instanceVo;

    private final StoneProcessVo processVo;

    private UserInfo nowUser;

    /**
     * 获取当前命令对象的所有带 @Autowired注解的属性，然后从spring上下文中获取到注入进去
     *
     */
    public NotifyNodeCmd(Object params) {
        super(params);
        this.param = (NotifyWaitNodeParam) params;
        this.processInstanceId = this.param.getProcessInstanceId();
        this.result = new WorkflowDetail();
        this.result.setProcessInstanceId(processInstanceId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(processInstanceId),"流程实例ID缺失");
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.param.getNodeName()),"节点名称缺失");
        this.instanceVo =  stoneProcessInstanceService.getByProcessInstanceId(this.processInstanceId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(this.instanceVo),"流程实例ID错误");
        this.processVo = stoneProcessService.getByProcessId(this.instanceVo.getProcessId());
        this.nowUser = UserHelper.getNowUser();
    }

    @Override
    public WorkflowDetail execute() {
        //查询待办的等待任务
        List<StoneTaskVo> taskVos = getWaitNodeTask();
        if(ObjectUtils.isEmpty(taskVos)){
            return this.result;
        }
        for(StoneTaskVo taskVo : taskVos){
            //获取后续节点及审批人信息
            WorkflowTaskNode nextNode = getNextNode(taskVo);
            //提交
            submit(nextNode,taskVo);
            //流程日志
            stoneProcessLogService.saveLog(processInstanceId,this.processVo.getProcessId(),taskVo.getNodeId(),taskVo.getNodeName(),taskVo.getTaskId(),
                    ProcessLogTypeEnum.SUBMIT.getType(),this.nowUser.getUserAccount(),"等待节点【"+nextNode.getNodeName()+"】处理成功");
        }
        loadWorkflowDetail();
        return this.result;
    }

    /**
     * 执行提交
     */
    private void submit(WorkflowTaskNode taskNode,StoneTaskVo taskVo){
        SubmitFlowParam param  = new SubmitFlowParam();
        param.setProcessId(this.processVo.getProcessId());
        param.setProcessInstanceId(this.processInstanceId);
        param.setAccount(this.nowUser.getUserAccount());
        param.setTaskId(taskVo.getTaskId());
        param.setFormData(formData(taskVo.getExt()));
        param.setSuggest("等待条件满足，自动流转到后续步骤");
        param.setNextNodes(taskNode.getNextNodes());
        new SubmitCmd(param).execute();
    }

    /**
     * 获取后续节点及审批人信息
     */
    private WorkflowTaskNode getNextNode(StoneTaskVo taskVo){
        String ext = taskVo.getExt();
        Map<String, Object> formData = formData(ext);
        QueryNextTaskParam nextTaskParam = new QueryNextTaskParam();
        nextTaskParam.setProcessId(this.processVo.getProcessId());
        nextTaskParam.setProcessInstanceId(this.processInstanceId);
        nextTaskParam.setAccount(this.nowUser.getUserAccount());
        nextTaskParam.setTaskId(taskVo.getTaskId());
        nextTaskParam.setFormData(formData);
        return new NextTaskNodeCmd(nextTaskParam).execute();
    }

    private Map<String,Object> formData(String ext){
        Map<String, Object> formData = new HashMap<>();
        try {
            formData = JsonHelper.jsonToMap(ext);
        }catch (Exception e){
            e.printStackTrace();
        }
        return formData;
    }


    private void loadWorkflowDetail(){
        WorkflowTask details = BeanCopyUtils.copyAndInit(instanceVo,WorkflowTask.class);
        details.setInitAccount(instanceVo.getStarter());
        details.setInitUserName(instanceVo.getStarterName());
        this.result.setDetails(details);
    }

    private List<StoneTaskVo> getWaitNodeTask(){
        List<String> nodeIds = nodeId();
        LambdaQueryWrapper<StoneTask> wrapper = stoneTaskService.wrapper()
                .eq(StoneTask::getProcessInstanceId, this.processInstanceId)
                .eq(StoneTask::getStatus, WorkflowConstant.TASK_STATUS_PROCESSING)
                .in(StoneTask::getNodeId, nodeIds);
        return stoneTaskService.queryVoList(wrapper);
    }

    private List<String> nodeId(){
        String processId = instanceVo.getProcessId();
        String content = new String(processVo.getContent());
        List<NodeModel> waitNodes = NodeUtils.getByNodeName(content, processId, this.param.getNodeName());
        return waitNodes.stream().map(NodeModel::nodeId).collect(Collectors.toList());
    }
}
